/****************************************************************************
 *
 * Copyright 2018 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/
/****************************************************************************
 * arch/arm/src/stm32/stm32_adc.c
 *
 *   Copyright (C) 2011, 2013 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *           Diego Sanchez <dsanchez@nx-engineering.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <tinyara/config.h>

#include <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <unistd.h>

#include <arch/board/board.h>
#include <tinyara/arch.h>
#include <tinyara/analog/adc.h>

#include "up_internal.h"
#include "up_arch.h"

#include "chip.h"
#include "stm32.h"
#include "stm32_adc.h"

/* ADC "upper half" support must be enabled */

#ifdef CONFIG_ADC

/* Some ADC peripheral must be enabled */

#if defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2) || defined(CONFIG_STM32_ADC3)

/* This implementation is for the STM32 F1, F2, and F4 only */

#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F20XX) || \
	defined(CONFIG_STM32_STM32F40XX)

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/
/* ADC interrupts ***********************************************************/

#ifdef CONFIG_STM32_STM32F10XX
#define ADC_SR_ALLINTS (ADC_SR_AWD | ADC_SR_EOC | ADC_SR_JEOC)
#else
#define ADC_SR_ALLINTS (ADC_SR_AWD | ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR)
#endif

#ifdef CONFIG_STM32_STM32F10XX
#define ADC_CR1_ALLINTS (ADC_CR1_AWDIE | ADC_CR1_EOCIE | ADC_CR1_JEOCIE)
#else
#define ADC_CR1_ALLINTS (ADC_CR1_AWDIE | ADC_CR1_EOCIE | ADC_CR1_JEOCIE | ADC_CR1_OVRIE)
#endif

/* The maximum number of channels that can be sampled.  If dma support is
 * not enabled, then only a single channel can be sampled.  Otherwise,
 * data overruns would occur.
 */

#ifdef CONFIG_ADC_DMA
#define ADC_MAX_SAMPLES 16
#else
#define ADC_MAX_SAMPLES 1
#endif

/****************************************************************************
 * Private Types
 ****************************************************************************/

/* This structure describes the state of one ADC block */

struct stm32_dev_s {
	uint8_t irq;				/* Interrupt generated by this ADC block */
	uint8_t nchannels;			/* Number of channels */
	uint8_t intf;				/* ADC interface number */
	uint8_t current;			/* Current ADC channel being converted */
#ifdef ADC_HAVE_TIMER
	uint8_t trigger;			/* Timer trigger channel: 0=CC1, 1=CC2, 2=CC3, 3=CC4, 4=TRGO */
#endif
	xcpt_t isr;					/* Interrupt handler for this ADC block */
	uint32_t base;				/* Base address of registers unique to this ADC block */
#ifdef ADC_HAVE_TIMER
	uint32_t tbase;				/* Base address of timer used by this ADC block */
	uint32_t extsel;			/* EXTSEL value used by this ADC block */
	uint32_t pclck;				/* The PCLK frequency that drives this timer */
	uint32_t freq;				/* The desired frequency of conversions */
#endif
	uint8_t chanlist[ADC_MAX_SAMPLES];
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* ADC Register access */

static uint32_t adc_getreg(struct stm32_dev_s *priv, int offset);
static void adc_putreg(struct stm32_dev_s *priv, int offset, uint32_t value);
#ifdef ADC_HAVE_TIMER
static uint16_t tim_getreg(struct stm32_dev_s *priv, int offset);
static void tim_putreg(struct stm32_dev_s *priv, int offset, uint16_t value);
static void adc_tim_dumpregs(struct stm32_dev_s *priv, FAR const char *msg);
#endif
static void adc_rccreset(struct stm32_dev_s *priv, bool reset);

/* ADC Interrupt Handler */

static int adc_interrupt(FAR struct adc_dev_s *dev);
#if defined(CONFIG_STM32_STM32F10XX) && (defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2))
static int adc12_interrupt(int irq, void *context);
#endif
#if defined(CONFIG_STM32_STM32F10XX) && defined(CONFIG_STM32_ADC3)
static int adc3_interrupt(int irq, void *context);
#endif
#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
static int adc123_interrupt(int irq, void *context);
#endif

/* ADC Driver Methods */

static void adc_reset(FAR struct adc_dev_s *dev);
static int adc_setup(FAR struct adc_dev_s *dev);
static void adc_shutdown(FAR struct adc_dev_s *dev);
static void adc_rxint(FAR struct adc_dev_s *dev, bool enable);
static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg);
static void adc_enable(FAR struct stm32_dev_s *priv, bool enable);

#ifdef ADC_HAVE_TIMER
static void adc_timstart(FAR struct stm32_dev_s *priv, bool enable);
static int adc_timinit(FAR struct stm32_dev_s *priv);
#endif

#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
static void adc_startconv(FAR struct stm32_dev_s *priv, bool enable);
#endif

/****************************************************************************
 * Private Data
 ****************************************************************************/

/* ADC interface operations */

static const struct adc_ops_s g_adcops = {
	.ao_reset = adc_reset,
	.ao_setup = adc_setup,
	.ao_shutdown = adc_shutdown,
	.ao_rxint = adc_rxint,
	.ao_ioctl = adc_ioctl,
};

/* ADC1 state */

#ifdef CONFIG_STM32_ADC1
static struct stm32_dev_s g_adcpriv1 = {
#ifdef CONFIG_STM32_STM32F10XX
	.irq = STM32_IRQ_ADC12,
	.isr = adc12_interrupt,
#else
	.irq = STM32_IRQ_ADC,
	.isr = adc123_interrupt,
#endif
	.intf = 1,
	.base = STM32_ADC1_BASE,
#ifdef ADC1_HAVE_TIMER
	.trigger = CONFIG_STM32_ADC1_TIMTRIG,
	.tbase = ADC1_TIMER_BASE,
	.extsel = ADC1_EXTSEL_VALUE,
	.pclck = ADC1_TIMER_PCLK_FREQUENCY,
	.freq = CONFIG_STM32_ADC1_SAMPLE_FREQUENCY,
#endif
};

static struct adc_dev_s g_adcdev1 = {
	.ad_ops = &g_adcops,
	.ad_priv = &g_adcpriv1,
};
#endif

/* ADC2 state */

#ifdef CONFIG_STM32_ADC2
static struct stm32_dev_s g_adcpriv2 = {
#ifdef CONFIG_STM32_STM32F10XX
	.irq = STM32_IRQ_ADC12,
	.isr = adc12_interrupt,
#else
	.irq = STM32_IRQ_ADC,
	.isr = adc123_interrupt,
#endif
	.intf = 2,
	.base = STM32_ADC2_BASE,
#ifdef ADC2_HAVE_TIMER
	.trigger = CONFIG_STM32_ADC2_TIMTRIG,
	.tbase = ADC2_TIMER_BASE,
	.extsel = ADC2_EXTSEL_VALUE,
	.pclck = ADC2_TIMER_PCLK_FREQUENCY,
	.freq = CONFIG_STM32_ADC2_SAMPLE_FREQUENCY,
#endif
};

static struct adc_dev_s g_adcdev2 = {
	.ad_ops = &g_adcops,
	.ad_priv = &g_adcpriv2,
};
#endif

/* ADC3 state */

#ifdef CONFIG_STM32_ADC3
static struct stm32_dev_s g_adcpriv3 = {
#ifdef CONFIG_STM32_STM32F10XX
	.irq = STM32_IRQ_ADC3,
	.isr = adc3_interrupt,
#else
	.irq = STM32_IRQ_ADC,
	.isr = adc123_interrupt,
#endif
	.intf = 3,
	.base = STM32_ADC3_BASE,
#ifdef ADC3_HAVE_TIMER
	.trigger = CONFIG_STM32_ADC3_TIMTRIG,
	.tbase = ADC3_TIMER_BASE,
	.extsel = ADC3_EXTSEL_VALUE,
	.pclck = ADC3_TIMER_PCLK_FREQUENCY,
	.freq = CONFIG_STM32_ADC3_SAMPLE_FREQUENCY,
#endif
};

static struct adc_dev_s g_adcdev3 = {
	.ad_ops = &g_adcops,
	.ad_priv = &g_adcpriv3,
};
#endif

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: adc_getreg
 *
 * Description:
 *   Read the value of an ADC register.
 *
 * Input Parameters:
 *   priv - A reference to the ADC block status
 *   offset - The offset to the register to read
 *
 * Returned Value:
 *
 ****************************************************************************/

static uint32_t adc_getreg(struct stm32_dev_s *priv, int offset)
{
	return getreg32(priv->base + offset);
}

/****************************************************************************
 * Name: adc_getreg
 *
 * Description:
 *   Read the value of an ADC register.
 *
 * Input Parameters:
 *   priv - A reference to the ADC block status
 *   offset - The offset to the register to read
 *
 * Returned Value:
 *
 ****************************************************************************/

static void adc_putreg(struct stm32_dev_s *priv, int offset, uint32_t value)
{
	putreg32(value, priv->base + offset);
}

/****************************************************************************
 * Name: tim_getreg
 *
 * Description:
 *   Read the value of an ADC timer register.
 *
 * Input Parameters:
 *   priv - A reference to the ADC block status
 *   offset - The offset to the register to read
 *
 * Returned Value:
 *   The current contents of the specified register
 *
 ****************************************************************************/

#ifdef ADC_HAVE_TIMER
static uint16_t tim_getreg(struct stm32_dev_s *priv, int offset)
{
	return getreg16(priv->tbase + offset);
}
#endif

/****************************************************************************
 * Name: tim_putreg
 *
 * Description:
 *   Read the value of an ADC timer register.
 *
 * Input Parameters:
 *   priv - A reference to the ADC block status
 *   offset - The offset to the register to read
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef ADC_HAVE_TIMER
static void tim_putreg(struct stm32_dev_s *priv, int offset, uint16_t value)
{
	putreg16(value, priv->tbase + offset);
}
#endif

/****************************************************************************
 * Name: adc_tim_dumpregs
 *
 * Description:
 *   Dump all timer registers.
 *
 * Input parameters:
 *   priv - A reference to the ADC block status
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef ADC_HAVE_TIMER
static void adc_tim_dumpregs(struct stm32_dev_s *priv, FAR const char *msg)
{
#if defined(CONFIG_DEBUG_ANALOG) && defined(CONFIG_DEBUG_VERBOSE)
	avdbg("%s:\n", msg);
	avdbg("  CR1: %04x CR2:  %04x SMCR:  %04x DIER:  %04x\n", tim_getreg(priv, STM32_GTIM_CR1_OFFSET), tim_getreg(priv, STM32_GTIM_CR2_OFFSET), tim_getreg(priv, STM32_GTIM_SMCR_OFFSET), tim_getreg(priv, STM32_GTIM_DIER_OFFSET));
	avdbg("   SR: %04x EGR:  0000 CCMR1: %04x CCMR2: %04x\n", tim_getreg(priv, STM32_GTIM_SR_OFFSET), tim_getreg(priv, STM32_GTIM_CCMR1_OFFSET), tim_getreg(priv, STM32_GTIM_CCMR2_OFFSET));
	avdbg(" CCER: %04x CNT:  %04x PSC:   %04x ARR:   %04x\n", tim_getreg(priv, STM32_GTIM_CCER_OFFSET), tim_getreg(priv, STM32_GTIM_CNT_OFFSET), tim_getreg(priv, STM32_GTIM_PSC_OFFSET), tim_getreg(priv, STM32_GTIM_ARR_OFFSET));
	avdbg(" CCR1: %04x CCR2: %04x CCR3:  %04x CCR4:  %04x\n", tim_getreg(priv, STM32_GTIM_CCR1_OFFSET), tim_getreg(priv, STM32_GTIM_CCR2_OFFSET), tim_getreg(priv, STM32_GTIM_CCR3_OFFSET), tim_getreg(priv, STM32_GTIM_CCR4_OFFSET));

	if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE) {
		avdbg("  RCR: %04x BDTR: %04x DCR:   %04x DMAR:  %04x\n", tim_getreg(priv, STM32_ATIM_RCR_OFFSET), tim_getreg(priv, STM32_ATIM_BDTR_OFFSET), tim_getreg(priv, STM32_ATIM_DCR_OFFSET), tim_getreg(priv, STM32_ATIM_DMAR_OFFSET));
	} else {
		avdbg("  DCR: %04x DMAR: %04x\n", tim_getreg(priv, STM32_GTIM_DCR_OFFSET), tim_getreg(priv, STM32_GTIM_DMAR_OFFSET));
	}
#endif
}
#endif

/****************************************************************************
 * Name: adc_timstart
 *
 * Description:
 *   Start (or stop) the timer counter
 *
 * Input Parameters:
 *   priv - A reference to the ADC block status
 *   enable - True: Start conversion
 *
 * Returned Value:
 *
 ****************************************************************************/

#ifdef ADC_HAVE_TIMER
static void adc_timstart(struct stm32_dev_s *priv, bool enable)
{
	uint16_t regval;

	avdbg("enable: %d\n", enable);
	regval = tim_getreg(priv, STM32_GTIM_CR1_OFFSET);

	if (enable) {
		/* Start the counter */

		regval |= ATIM_CR1_CEN;
	}

	else {
		/* Disable the counter */

		regval &= ~ATIM_CR1_CEN;
	}
	tim_putreg(priv, STM32_GTIM_CR1_OFFSET, regval);
}
#endif

/****************************************************************************
 * Name: adc_timinit
 *
 * Description:
 *   Initialize the timer that drivers the ADC sampling for this channel using
 *   the pre-calculated timer divider definitions.
 *
 * Input Parameters:
 *   priv - A reference to the ADC block status
 *
 * Returned Value:
 *   Zero on success; a negated errno value on failure.
 *
 ****************************************************************************/

#ifdef ADC_HAVE_TIMER
static int adc_timinit(FAR struct stm32_dev_s *priv)
{
	uint32_t prescaler;
	uint32_t reload;
	uint32_t regval;
	uint32_t timclk;

	uint16_t cr1;
	uint16_t cr2;
	uint16_t ccmr1;
	uint16_t ccmr2;
	uint16_t ocmode1;
	uint16_t ocmode2;
	uint16_t ccenable;
	uint16_t ccer;
	uint16_t egr;

	avdbg("Initializing timers extsel = %d\n", priv->extsel);

	/* If the timer base address is zero, then this ADC was not configured to
	 * use a timer.
	 */

	regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);

#ifdef CONFIG_STM32_STM32F10XX
	if (!priv->tbase) {
		/* Configure the ADC to use the selected timer and timer channel as the trigger
		 * EXTTRIG: External Trigger Conversion mode for regular channels DISABLE
		 */

		regval &= ~ADC_CR2_EXTTRIG;
		adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
		return OK;
	} else {
		regval |= ADC_CR2_EXTTRIG;
	}
#endif

	/* EXTSEL selection: These bits select the external event used to trigger
	 * the start of conversion of a regular group.  NOTE:
	 *
	 * - The position with with of the EXTSEL field varies from one STM32 MCU
	 *   to another.
	 * - The width of the EXTSEL field varies from one STM3 MCU to another.
	 * - The value in priv->extsel is already shifted into the correct bit position.
	 */

	regval &= ~ADC_CR2_EXTSEL_MASK;
	regval |= priv->extsel;
	adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);

	/* Configure the timer channel to drive the ADC */

	/* Caculate optimal values for the timer prescaler and for the timer reload
	 * register.  If freq is the desired frequency, then
	 *
	 *   reload = timclk / freq
	 *   reload = (pclck / prescaler) / freq
	 *
	 * There are many solutions to do this, but the best solution will be the
	 * one that has the largest reload value and the smallest prescaler value.
	 * That is the solution that should give us the most accuracy in the timer
	 * control.  Subject to:
	 *
	 *   0 <= prescaler  <= 65536
	 *   1 <= reload <= 65535
	 *
	 * So ( prescaler = pclck / 65535 / freq ) would be optimal.
	 */

	prescaler = (priv->pclck / priv->freq + 65534) / 65535;

	/* We need to decrement the prescaler value by one, but only, the value does
	 * not underflow.
	 */

	if (prescaler < 1) {
		adbg("WARNING: Prescaler underflowed.\n");
		prescaler = 1;
	}

	/* Check for overflow */

	else if (prescaler > 65536) {
		adbg("WARNING: Prescaler overflowed.\n");
		prescaler = 65536;
	}

	timclk = priv->pclck / prescaler;

	reload = timclk / priv->freq;
	if (reload < 1) {
		adbg("WARNING: Reload value underflowed.\n");
		reload = 1;
	} else if (reload > 65535) {
		adbg("WARNING: Reload value overflowed.\n");
		reload = 65535;
	}

	/* Set up the timer CR1 register */

	cr1 = tim_getreg(priv, STM32_GTIM_CR1_OFFSET);

	/* Disable the timer until we get it configured */

	adc_timstart(priv, false);

	/* Select the Counter Mode == count up:
	 *
	 * ATIM_CR1_EDGE: The counter counts up or down depending on the
	 *                direction bit(DIR).
	 * ATIM_CR1_DIR: 0: count up, 1: count down
	 */

	cr1 &= ~(ATIM_CR1_DIR | ATIM_CR1_CMS_MASK);
	cr1 |= ATIM_CR1_EDGE;

	/* Set the clock division to zero for all */

	cr1 &= ~GTIM_CR1_CKD_MASK;
	tim_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1);

	/* Set the reload and prescaler values */

	tim_putreg(priv, STM32_GTIM_PSC_OFFSET, prescaler - 1);
	tim_putreg(priv, STM32_GTIM_ARR_OFFSET, reload);

	/* Clear the advanced timers repitition counter in TIM1 */

	if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE) {
		tim_putreg(priv, STM32_ATIM_RCR_OFFSET, 0);
		tim_putreg(priv, STM32_ATIM_BDTR_OFFSET, ATIM_BDTR_MOE);	/* Check me */
	}

	/* TIMx event generation: Bit 0 UG: Update generation */

	tim_putreg(priv, STM32_GTIM_EGR_OFFSET, ATIM_EGR_UG);

	/* Handle channel specific setup */

	ocmode1 = 0;
	ocmode2 = 0;

	switch (priv->trigger) {
	case 0: {					/* TimerX CC1 event */
		ccenable = ATIM_CCER_CC1E;
		ocmode1 = (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC1S_SHIFT) | (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR1_OC1M_SHIFT) | ATIM_CCMR1_OC1PE;

		/* Set the event CC1 */

		egr = ATIM_EGR_CC1G;

		/* Set the duty cycle by writing to the CCR register for this channel */

		tim_putreg(priv, STM32_GTIM_CCR1_OFFSET, (uint16_t)(reload >> 1));
	}
	break;

	case 1: {					/* TimerX CC2 event */
		ccenable = ATIM_CCER_CC2E;
		ocmode1 = (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC2S_SHIFT) | (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR1_OC2M_SHIFT) | ATIM_CCMR1_OC2PE;

		/* Set the event CC2 */

		egr = ATIM_EGR_CC2G;

		/* Set the duty cycle by writing to the CCR register for this channel */

		tim_putreg(priv, STM32_GTIM_CCR2_OFFSET, (uint16_t)(reload >> 1));
	}
	break;

	case 2: {					/* TimerX CC3 event */
		ccenable = ATIM_CCER_CC3E;
		ocmode2 = (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR2_CC3S_SHIFT) | (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR2_OC3M_SHIFT) | ATIM_CCMR2_OC3PE;

		/* Set the event CC3 */

		egr = ATIM_EGR_CC3G;

		/* Set the duty cycle by writing to the CCR register for this channel */

		tim_putreg(priv, STM32_GTIM_CCR3_OFFSET, (uint16_t)(reload >> 1));
	}
	break;

	case 3: {					/* TimerX CC4 event */
		ccenable = ATIM_CCER_CC4E;
		ocmode2 = (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR2_CC4S_SHIFT) | (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR2_OC4M_SHIFT) | ATIM_CCMR2_OC4PE;

		/* Set the event CC4 */

		egr = ATIM_EGR_CC4G;

		/* Set the duty cycle by writing to the CCR register for this channel */

		tim_putreg(priv, STM32_GTIM_CCR4_OFFSET, (uint16_t)(reload >> 1));
	}
	break;

	case 4: {					/* TimerX TRGO event */
		/* TODO: TRGO support not yet implemented */
		/* Set the event TRGO */

		ccenable = 0;
		egr = GTIM_EGR_TG;

		/* Set the duty cycle by writing to the CCR register for this channel */

		tim_putreg(priv, STM32_GTIM_CCR4_OFFSET, (uint16_t)(reload >> 1));
	}
	break;

	default:
		adbg("No such trigger: %d\n", priv->trigger);
		return -EINVAL;
	}

	/* Disable the Channel by resetting the CCxE Bit in the CCER register */

	ccer = tim_getreg(priv, STM32_GTIM_CCER_OFFSET);
	ccer &= ~ccenable;
	tim_putreg(priv, STM32_GTIM_CCER_OFFSET, ccer);

	/* Fetch the CR2, CCMR1, and CCMR2 register (already have cr1 and ccer) */

	cr2 = tim_getreg(priv, STM32_GTIM_CR2_OFFSET);
	ccmr1 = tim_getreg(priv, STM32_GTIM_CCMR1_OFFSET);
	ccmr2 = tim_getreg(priv, STM32_GTIM_CCMR2_OFFSET);

	/* Reset the Output Compare Mode Bits and set the select output compare mode */

	ccmr1 &= ~(ATIM_CCMR1_CC1S_MASK | ATIM_CCMR1_OC1M_MASK | ATIM_CCMR1_OC1PE | ATIM_CCMR1_CC2S_MASK | ATIM_CCMR1_OC2M_MASK | ATIM_CCMR1_OC2PE);
	ccmr2 &= ~(ATIM_CCMR2_CC3S_MASK | ATIM_CCMR2_OC3M_MASK | ATIM_CCMR2_OC3PE | ATIM_CCMR2_CC4S_MASK | ATIM_CCMR2_OC4M_MASK | ATIM_CCMR2_OC4PE);
	ccmr1 |= ocmode1;
	ccmr2 |= ocmode2;

	/* Reset the output polarity level of all channels (selects high polarity) */

	ccer &= ~(ATIM_CCER_CC1P | ATIM_CCER_CC2P | ATIM_CCER_CC3P | ATIM_CCER_CC4P);

	/* Enable the output state of the selected channel (only) */

	ccer &= ~(ATIM_CCER_CC1E | ATIM_CCER_CC2E | ATIM_CCER_CC3E | ATIM_CCER_CC4E);
	ccer |= ccenable;

	if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE) {
		/* Reset output N polarity level, output N state, output compre state,
		 * output compare N idle state.
		 */
#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
		ccer &= ~(ATIM_CCER_CC1NE | ATIM_CCER_CC1NP | ATIM_CCER_CC2NE | ATIM_CCER_CC2NP | ATIM_CCER_CC3NE | ATIM_CCER_CC3NP | ATIM_CCER_CC4NP);
#else
		ccer &= ~(ATIM_CCER_CC1NE | ATIM_CCER_CC1NP | ATIM_CCER_CC2NE | ATIM_CCER_CC2NP | ATIM_CCER_CC3NE | ATIM_CCER_CC3NP);
#endif

		/* Reset the output compare and output compare N IDLE State */

		cr2 &= ~(ATIM_CR2_OIS1 | ATIM_CR2_OIS1N | ATIM_CR2_OIS2 | ATIM_CR2_OIS2N | ATIM_CR2_OIS3 | ATIM_CR2_OIS3N | ATIM_CR2_OIS4);
	}
#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
	else {
		ccer &= ~(GTIM_CCER_CC1NP | GTIM_CCER_CC2NP | GTIM_CCER_CC3NP);
	}
#endif

	/* Save the modified register values */

	tim_putreg(priv, STM32_GTIM_CR2_OFFSET, cr2);
	tim_putreg(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1);
	tim_putreg(priv, STM32_GTIM_CCMR2_OFFSET, ccmr2);
	tim_putreg(priv, STM32_GTIM_CCER_OFFSET, ccer);
	tim_putreg(priv, STM32_GTIM_EGR_OFFSET, egr);

	/* Set the ARR Preload Bit */

	cr1 = tim_getreg(priv, STM32_GTIM_CR1_OFFSET);
	cr1 |= GTIM_CR1_ARPE;
	tim_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1);

	/* Enable the timer counter
	 * All but the CEN bit with the default config in CR1
	 */

	adc_timstart(priv, true);

	adc_tim_dumpregs(priv, "After starting Timers");

	return OK;
}
#endif

/****************************************************************************
 * Name: adc_startconv
 *
 * Description:
 *   Start (or stop) the ADC conversion process in DMA mode
 *
 * Input Parameters:
 *   priv - A reference to the ADC block status
 *   enable - True: Start conversion
 *
 * Returned Value:
 *
 ****************************************************************************/

#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
static void adc_startconv(struct stm32_dev_s *priv, bool enable)
{
	uint32_t regval;

	avdbg("enable: %d\n", enable);

	regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);
	if (enable) {
		/* Start conversion of regular channles */

		regval |= ADC_CR2_SWSTART;
	} else {
		/* Disable the conversion of regular channels */

		regval &= ~ADC_CR2_SWSTART;
	}
	adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
}
#endif

/****************************************************************************
 * Name: adc_rccreset
 *
 * Description:
 *   Deinitializes the ADCx peripheral registers to their default
 *   reset values. It could set all the ADCs configured.
 *
 * Input Parameters:
 *   regaddr - The register to read
 *   reset - Condition, set or reset
 *
 * Returned Value:
 *
 ****************************************************************************/

static void adc_rccreset(struct stm32_dev_s *priv, bool reset)
{
	irqstate_t flags;
	uint32_t regval;
	uint32_t adcbit;

	/* Pick the appropriate bit in the APB2 reset register */

#ifdef CONFIG_STM32_STM32F10XX
	/* For the STM32 F1, there is an individual bit to reset each ADC. */

	switch (priv->intf) {
#ifdef CONFIG_STM32_ADC1
	case 1:
		adcbit = RCC_APB2RSTR_ADC1RST;
		break;
#endif
#ifdef CONFIG_STM32_ADC2
	case 2:
		adcbit = RCC_APB2RSTR_ADC2RST;
		break;
#endif
#ifdef CONFIG_STM32_ADC3
	case 3:
		adcbit = RCC_APB2RSTR_ADC3RST;
		break;
#endif
	default:
		return;
	}

#else
	/* For the STM32 F4, there is one common reset for all ADC block.
	 * THIS will probably cause some problems!
	 */

	adcbit = RCC_APB2RSTR_ADCRST;
#endif

	/* Disable interrupts.  This is necessary because the APB2RTSR register
	 * is used by several different drivers.
	 */

	flags = irqsave();

	/* Set or clear the selected bit in the APB2 reset register */

	regval = getreg32(STM32_RCC_APB2RSTR);
	if (reset) {
		/* Enable  ADC reset state */

		regval |= adcbit;
	} else {
		/* Release ADC from reset state */

		regval &= ~adcbit;
	}
	putreg32(regval, STM32_RCC_APB2RSTR);
	irqrestore(flags);
}

/*******************************************************************************
 * Name: adc_enable
 *
 * Description    : Enables or disables the specified ADC peripheral.
 *                  Also, starts a conversion when the ADC is not
 *                  triggered by timers
 *
 * Input Parameters:
 *
 *   enable - true:  enable ADC conversion
 *            false: disable ADC conversion
 *
 * Returned Value:
 *
 *******************************************************************************/

static void adc_enable(FAR struct stm32_dev_s *priv, bool enable)
{
	uint32_t regval;

	avdbg("enable: %d\n", enable);

	regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);
	if (enable) {
		regval |= ADC_CR2_ADON;
	} else {
		regval &= ~ADC_CR2_ADON;
	}
	adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
}

/****************************************************************************
 * Name: adc_reset
 *
 * Description:
 *   Reset the ADC device.  Called early to initialize the hardware. This
 *   is called, before adc_setup() and on error conditions.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

static void adc_reset(FAR struct adc_dev_s *dev)
{
	FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
	irqstate_t flags;
	uint32_t regval;
	int offset;
	int i;
#ifdef ADC_HAVE_TIMER
	int ret;
#endif

	avdbg("intf: ADC%d\n", priv->intf);
	flags = irqsave();

	/* Enable ADC reset state */

	adc_rccreset(priv, true);

	/* Release ADC from reset state */

	adc_rccreset(priv, false);

	/* Initialize the ADC data structures */

	/* Initialize the watchdog high threshold register */

	adc_putreg(priv, STM32_ADC_HTR_OFFSET, 0x00000fff);

	/* Initialize the watchdog low threshold register */

	adc_putreg(priv, STM32_ADC_LTR_OFFSET, 0x00000000);

	/* Initialize the same sample time for each ADC 55.5 cycles
	 *
	 * During sample cycles channel selection bits must remain unchanged.
	 *
	 *   000:   1.5 cycles
	 *   001:   7.5 cycles
	 *   010:  13.5 cycles
	 *   011:  28.5 cycles
	 *   100:  41.5 cycles
	 *   101:  55.5 cycles
	 *   110:  71.5 cycles
	 *   111: 239.5 cycles
	 */

	adc_putreg(priv, STM32_ADC_SMPR1_OFFSET, 0x00b6db6d);
	adc_putreg(priv, STM32_ADC_SMPR2_OFFSET, 0x00b6db6d);

	/* ADC CR1 Configuration */

	regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET);

	/* Set mode configuration (Independent mode) */

#ifdef CONFIG_STM32_STM32F10XX
	regval |= ADC_CR1_IND;
#endif

	/* Initialize the Analog watchdog enable */

	regval |= ADC_CR1_AWDEN;
	regval |= (priv->chanlist[0] << ADC_CR1_AWDCH_SHIFT);

	/* Enable interrupt flags */

	regval |= ADC_CR1_ALLINTS;

#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)

	/* Enable or disable Overrun interrupt */

	regval &= ~ADC_CR1_OVRIE;

	/* Set the resolution of the conversion */

	regval |= ADC_CR1_RES_12BIT;
#endif

	adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval);

	/* ADC CR2 Configuration */

	regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);

	/* Clear CONT, continuous mode disable */

	regval &= ~ADC_CR2_CONT;

	/* Set ALIGN (Right = 0) */

	regval &= ~ADC_CR2_ALIGN;

#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
	/* External trigger enable for regular channels */

	regval |= ADC_CR2_EXTEN_RISING;
#endif

	adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);

	/* Configuration of the channel conversions */

	regval = adc_getreg(priv, STM32_ADC_SQR3_OFFSET) & ADC_SQR3_RESERVED;
	for (i = 0, offset = 0; i < priv->nchannels && i < 6; i++, offset += 5) {
		regval |= (uint32_t)priv->chanlist[i] << offset;
	}
	adc_putreg(priv, STM32_ADC_SQR3_OFFSET, regval);

	regval = adc_getreg(priv, STM32_ADC_SQR2_OFFSET) & ADC_SQR2_RESERVED;
	for (i = 6, offset = 0; i < priv->nchannels && i < 12; i++, offset += 5) {
		regval |= (uint32_t)priv->chanlist[i] << offset;
	}
	adc_putreg(priv, STM32_ADC_SQR2_OFFSET, regval);

	regval = adc_getreg(priv, STM32_ADC_SQR1_OFFSET) & ADC_SQR1_RESERVED;
	for (i = 12, offset = 0; i < priv->nchannels && i < 16; i++, offset += 5) {
		regval |= (uint32_t)priv->chanlist[i] << offset;
	}

	/* ADC CCR configuration */

#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
	regval = getreg32(STM32_ADC_CCR);
	regval &= ~(ADC_CCR_MULTI_MASK | ADC_CCR_DELAY_MASK | ADC_CCR_DDS | ADC_CCR_DMA_MASK | ADC_CCR_ADCPRE_MASK | ADC_CCR_VBATE | ADC_CCR_TSVREFE);
	regval |= (ADC_CCR_MULTI_NONE | ADC_CCR_DMA_DISABLED | ADC_CCR_ADCPRE_DIV2);
	putreg32(regval, STM32_ADC_CCR);
#endif

	/* Set the number of conversions */

	DEBUGASSERT(priv->nchannels <= ADC_MAX_SAMPLES);

	regval |= (((uint32_t)priv->nchannels - 1) << ADC_SQR1_L_SHIFT);
	adc_putreg(priv, STM32_ADC_SQR1_OFFSET, regval);

	/* Set the channel index of the first conversion */

	priv->current = 0;

	/* Set ADON to wake up the ADC from Power Down state. */

	adc_enable(priv, true);

#ifdef ADC_HAVE_TIMER
	ret = adc_timinit(priv);
	if (ret != OK) {
		adbg("Error initializing the timers\n");
	}
#else
#ifdef CONFIG_STM32_STM32F10XX
	/* Set ADON (Again) to start the conversion.  Only if Timers are not
	 * configured as triggers
	 */

	adc_enable(priv, true);
#else
	adc_startconv(priv, true);
#endif							/* CONFIG_STM32_STM32F10XX */
#endif							/* ADC_HAVE_TIMER */

	irqrestore(flags);

	avdbg("SR:   0x%08x CR1:  0x%08x CR2:  0x%08x\n", adc_getreg(priv, STM32_ADC_SR_OFFSET), adc_getreg(priv, STM32_ADC_CR1_OFFSET), adc_getreg(priv, STM32_ADC_CR2_OFFSET));
	avdbg("SQR1: 0x%08x SQR2: 0x%08x SQR3: 0x%08x\n", adc_getreg(priv, STM32_ADC_SQR1_OFFSET), adc_getreg(priv, STM32_ADC_SQR2_OFFSET), adc_getreg(priv, STM32_ADC_SQR3_OFFSET));
#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
	avdbg("CCR:  0x%08x\n", getreg32(STM32_ADC_CCR));
#endif
}

/****************************************************************************
 * Name: adc_setup
 *
 * Description:
 *   Configure the ADC. This method is called the first time that the ADC
 *   device is opened.  This will occur when the port is first opened.
 *   This setup includes configuring and attaching ADC interrupts.  Interrupts
 *   are all disabled upon return.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

static int adc_setup(FAR struct adc_dev_s *dev)
{
	FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
	int ret;

	/* Attach the ADC interrupt */

	ret = irq_attach(priv->irq, priv->isr, NULL);
	if (ret == OK) {
		/* Make sure that the ADC device is in the powered up, reset state */

		adc_reset(dev);

		/* Enable the ADC interrupt */

		avdbg("Enable the ADC interrupt: irq=%d\n", priv->irq);
		up_enable_irq(priv->irq);
	}

	return ret;
}

/****************************************************************************
 * Name: adc_shutdown
 *
 * Description:
 *   Disable the ADC.  This method is called when the ADC device is closed.
 *   This method reverses the operation the setup method.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

static void adc_shutdown(FAR struct adc_dev_s *dev)
{
	FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;

	/* Disable ADC interrupts and detach the ADC interrupt handler */

	up_disable_irq(priv->irq);
	irq_detach(priv->irq);

	/* Disable and reset the ADC module */

	adc_rccreset(priv, true);
}

/****************************************************************************
 * Name: adc_rxint
 *
 * Description:
 *   Call to enable or disable RX interrupts.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

static void adc_rxint(FAR struct adc_dev_s *dev, bool enable)
{
	FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
	uint32_t regval;

	avdbg("intf: %d enable: %d\n", priv->intf, enable);

	regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET);
	if (enable) {
		/* Enable the end-of-conversion ADC and analog watchdog interrupts */

		regval |= ADC_CR1_ALLINTS;
	} else {
		/* Disable all ADC interrupts */

		regval &= ~ADC_CR1_ALLINTS;
	}
	adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval);
}

/****************************************************************************
 * Name: adc_ioctl
 *
 * Description:
 *   All ioctl calls will be routed through this method.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
{
	return -ENOTTY;
}

/****************************************************************************
 * Name: adc_interrupt
 *
 * Description:
 *   Common ADC interrupt handler.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

static int adc_interrupt(FAR struct adc_dev_s *dev)
{
	FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
	uint32_t adcsr;
	int32_t value;

	/* Identifies the interruption AWD, OVR or EOC */

	adcsr = adc_getreg(priv, STM32_ADC_SR_OFFSET);
	if ((adcsr & ADC_SR_AWD) != 0) {
		alldbg("WARNING: Analog Watchdog, Value converted out of range!\n");
	}
#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
	if ((adcsr & ADC_SR_OVR) != 0) {
		alldbg("WARNING: Overrun has ocurred!\n");
	}
#endif

	/* EOC: End of conversion */

	if ((adcsr & ADC_SR_EOC) != 0) {
		/* Read the converted value and clear EOC bit
		 * (It is cleared by reading the ADC_DR)
		 */

		value = adc_getreg(priv, STM32_ADC_DR_OFFSET);
		value &= ADC_DR_DATA_MASK;

		/* Give the ADC data to the ADC driver.  adc_receive accepts 3 parameters:
		 *
		 * 1) The first is the ADC device instance for this ADC block.
		 * 2) The second is the channel number for the data, and
		 * 3) The third is the converted data for the channel.
		 */

		adc_receive(dev, priv->chanlist[priv->current], value);

		/* Set the channel number of the next channel that will complete conversion */

		priv->current++;

		if (priv->current >= priv->nchannels) {
			/* Restart the conversion sequence from the beginning */

			priv->current = 0;
		}
	}

	return OK;
}

/****************************************************************************
 * Name: adc12_interrupt
 *
 * Description:
 *   ADC12 interrupt handler for the STM32 F1 family.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

#if defined(CONFIG_STM32_STM32F10XX) && (defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2))
static int adc12_interrupt(int irq, void *context)
{
	uint32_t regval;
	uint32_t pending;

	/* Check for pending ADC1 interrupts */

#ifdef CONFIG_STM32_ADC1
	regval = getreg32(STM32_ADC1_SR);
	pending = regval & ADC_SR_ALLINTS;
	if (pending != 0) {
		adc_interrupt(&g_adcdev1);
		regval &= ~pending;
		putreg32(regval, STM32_ADC1_SR);
	}
#endif

	/* Check for pending ADC2 interrupts */

#ifdef CONFIG_STM32_ADC2
	regval = getreg32(STM32_ADC2_SR);
	pending = regval & ADC_SR_ALLINTS;
	if (pending != 0) {
		adc_interrupt(&g_adcdev2);
		regval &= ~pending;
		putreg32(regval, STM32_ADC2_SR);
	}
#endif
	return OK;
}
#endif

/****************************************************************************
 * Name: adc3_interrupt
 *
 * Description:
 *   ADC1/2 interrupt handler for the STM32 F1 family.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

#if defined(CONFIG_STM32_STM32F10XX) && defined(CONFIG_STM32_ADC3)
static int adc3_interrupt(int irq, void *context)
{
	uint32_t regval;
	uint32_t pending;

	/* Check for pending ADC3 interrupts */

	regval = getreg32(STM32_ADC3_SR);
	pending = regval & ADC_SR_ALLINTS;
	if (pending != 0) {
		adc_interrupt(&g_adcdev3);
		regval &= ~pending;
		putreg32(regval, STM32_ADC3_SR);
	}

	return OK;
}
#endif

/****************************************************************************
 * Name: adc123_interrupt
 *
 * Description:
 *   ADC1/2/3 interrupt handler for the STM32 F4 family.
 *
 * Input Parameters:
 *
 * Returned Value:
 *
 ****************************************************************************/

#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)
static int adc123_interrupt(int irq, void *context)
{
	uint32_t regval;
	uint32_t pending;

	/* Check for pending ADC1 interrupts */

#ifdef CONFIG_STM32_ADC1
	regval = getreg32(STM32_ADC1_SR);
	pending = regval & ADC_SR_ALLINTS;
	if (pending != 0) {
		adc_interrupt(&g_adcdev1);
		regval &= ~pending;
		putreg32(regval, STM32_ADC1_SR);
	}
#endif

	/* Check for pending ADC2 interrupts */

#ifdef CONFIG_STM32_ADC2
	regval = getreg32(STM32_ADC2_SR);
	pending = regval & ADC_SR_ALLINTS;
	if (pending != 0) {
		adc_interrupt(&g_adcdev2);
		regval &= ~pending;
		putreg32(regval, STM32_ADC2_SR);
	}
#endif

	/* Check for pending ADC3 interrupts */

#ifdef CONFIG_STM32_ADC3
	regval = getreg32(STM32_ADC3_SR);
	pending = regval & ADC_SR_ALLINTS;
	if (pending != 0) {
		adc_interrupt(&g_adcdev3);
		regval &= ~pending;
		putreg32(regval, STM32_ADC3_SR);
	}
#endif
	return OK;
}
#endif

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: stm32_adcinitialize
 *
 * Description:
 *   Initialize the ADC.
 *
 *   The logic is, save nchannels : # of channels (conversions) in ADC_SQR1_L
 *   Then, take the chanlist array and store it in the SQR Regs,
 *     chanlist[0] -> ADC_SQR3_SQ1
 *     chanlist[1] -> ADC_SQR3_SQ2
 *     ...
 *     chanlist[15]-> ADC_SQR1_SQ16
 *
 *   up to
 *     chanlist[nchannels]
 *
 * Input Parameters:
 *   intf      - Could be {1,2,3} for ADC1, ADC2, or ADC3
 *   chanlist  - The list of channels
 *   nchannels - Number of channels
 *
 * Returned Value:
 *   Valid ADC device structure reference on succcess; a NULL on failure
 *
 ****************************************************************************/

struct adc_dev_s *stm32_adcinitialize(int intf, const uint8_t *chanlist, int nchannels)
{
	FAR struct adc_dev_s *dev;
	FAR struct stm32_dev_s *priv;

	avdbg("intf: %d nchannels: %d\n", intf, nchannels);

#ifdef CONFIG_STM32_ADC1
	if (intf == 1) {
		avdbg("ADC1 Selected\n");
		dev = &g_adcdev1;
	} else
#endif
#ifdef CONFIG_STM32_ADC2
		if (intf == 2) {
			avdbg("ADC2 Selected\n");
			dev = &g_adcdev2;
		} else
#endif
#ifdef CONFIG_STM32_ADC3
			if (intf == 3) {
				avdbg("ADC3 Selected\n");
				dev = &g_adcdev3;
			} else
#endif
			{
				adbg("No ADC interface defined\n");
				return NULL;
			}

	/* Configure the selected ADC */

	priv = dev->ad_priv;

	DEBUGASSERT(nchannels <= ADC_MAX_SAMPLES);
	priv->nchannels = nchannels;

	memcpy(priv->chanlist, chanlist, nchannels);
	return dev;
}

#endif							/* CONFIG_STM32_STM32F10XX || CONFIG_STM32_STM32F20XX || CONFIG_STM32_STM32F40XX */
#endif							/* CONFIG_STM32_ADC || CONFIG_STM32_ADC2 || CONFIG_STM32_ADC3 */
#endif							/* CONFIG_ADC */
